#include "MetaRace.h"

#include "Caracteristique.h"

#include "lib.h"

#include "ATL.h"

//#include "debugstreams.h"

#include <vector>

#include "Humain.h"
#include "DemiElfe.h"
#include "DemiOgre.h"
#include "DemiOrque.h"
#include "Elfe.h"
#include "Gnome.h"
#include "Hobbit.h"
#include "Nain.h"

#include "CodesErreurs.h"

using namespace std;

const string MetaRace::fichierSource( "Joueur0/Donnees/ListeRace.xml" );

/**
 * Constructeur par standard. Uniquement le nom de la Race est
 * initialisé ici. Les autres informations sur la Race sont lues
 * depuis le fichier par la méthode lireFichier().
 *
 * @param[in] nomRace nom de la race.
 */
MetaRace::MetaRace(const string& nomRace)
  : Race( nomRace ), Lecture( fichierSource )
{}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
MetaRace::~MetaRace()
{}

/**
 * Lit le fichier et charge la Race correspondante dans l'instance
 * courante.
 *
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher les messages de début et de fin de
 * méthode. \n
 * \e INFO permet d'afficher le nom de la race, le nombre de PP
 * disponibles pour la création, le nom et le coût des capacités et
 * ensembles, les ajustements aux traits.

 *
 * @return StatusCode
 */
StatusCode MetaRace::lireFichier()
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "MetaRace::lireFichier ==> début" << endmsg;

  StatusCode sc( Lecture.lireDocument() );
  if ( ! sc )
    {
      error() << sc.message() << endmsg;
      func() << "MetaRace::lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return sc;
    }

  const ElementDOM* noeud;

  noeud = Lecture.premierEnfant( Lecture.premierEnfant() );

  if ( noeud == 0 )
    {
      func() << "MetaRace::lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, - CodesErreurs::AucunEnfantTrouve,
			 "Aucun enfant trouvé" );
    }

  do
    {
      if ( noeud -> balise() != "Race" )
	{
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::AutreBaliseTrouvee,
			     "On attendait \"Race\"" );
	}

      if ( Lecture.premierEnfant( noeud ) -> donnees() == Type )
	break;

      noeud = Lecture.prochainFrere( noeud );
    }
  while ( noeud != 0 );

  if ( noeud == 0 )
    {
      func() << "MetaRace::lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, - CodesErreurs::ElementManquant,
			 "Impossible de trouver " + Type );
    }

  noeud = Lecture.premierEnfant( noeud );

  info() << noeud -> balise() << " : " << noeud -> donnees() << endmsg;

  noeud = Lecture.prochainFrere( noeud );

  info() << "Points alloués pour la création : " << noeud -> donnees()
	 << endmsg;

  if ( ! lireString( noeud -> donnees(), NombrePPsAlloues ) )
    {
      func() << "MetaRace::lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "Impossible de lire le nbr de pp alloués pour la race" );
    }

  noeud = Lecture.prochainFrere( noeud );

  unsigned nbrElement( 0 ), uIntTmp, elementsAttendus( 0 );

  Prerequis tmp;

  if ( noeud -> balise() == "Limites" )
    {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre de prérequis" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      const ElementDOM *prerequis( Lecture.premierEnfant( noeud ) );

      do
	{
	  if ( ! lireString( prerequis -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << prerequis -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;

	  sc = lirePrerequis( prerequis );
	  if ( ! sc )
	    {
	      sc.afficher();
	      func() << "MetaRace::lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les prérequis" );
	    }
	  info() << PrerequisRaciaux.back() << endmsg;

	  prerequis = Lecture.prochainFrere( prerequis );
	}
      while ( prerequis != 0 );

      if ( nbrElement + 1 != elementsAttendus )
	warning() << "Collecté " << nbrElement + 1 << " prérequis, alors"
		  << "que " << elementsAttendus << " étaients attendus"
		  << endmsg;
      else if ( PrerequisRaciaux.size() != elementsAttendus )
	warning() << "Problème de lecture des prérequis raciaux : "
		  << "on attend " << elementsAttendus << " items, mais "
		  << PrerequisRaciaux.size() << " ont été trouvés" << endmsg;

      nbrElement = 0;

      noeud = Lecture.prochainFrere( noeud );
    }

  const ElementDOM* capa( Lecture.premierEnfant( noeud ) );
  CapaciteRaciale tmpCapa;

  if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
    {
      error() << "Ne peut pas lire le nombre de capacités" << endmsg;
      func() << "MetaRace::lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureAttributUint,
			 "lireFichier : Impossible de lire le nombre d'élément attendus" );
    }

  do
    {
      if ( ! lireString( capa -> attribut( "num" ), uIntTmp ) )
	warning() << "Ne peut pas lire l'attribut \"num\" de "
		  << capa -> balise() << endmsg;
      else if ( uIntTmp > nbrElement )
	nbrElement = uIntTmp;

      sc = lireCapa( capa );
      if ( ! sc )
	{
	  error() << sc.message() << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurConstructionObjetComplexe,
			     "lireFichier : impossible d'initialiser les capacités raciales" );
	}

      info() << CapacitesRaciales.back() << endmsg;

      capa = Lecture.prochainFrere( capa );
    }
  while ( capa != 0 );

  if ( CapacitesRaciales.size() != nbrElement + 1 )
    warning() << "Problème de lecture des capacités raciales : "
	      << "on attend " << nbrElement + 1 << " items, mais "
	      << CapacitesRaciales.size() << " ont été trouvés" << endmsg;

  NombreTotalCapacites = CapacitesRaciales.size();
  
  nbrElement = 0;

  noeud = Lecture.prochainFrere( noeud );

  if ( noeud != 0 && noeud -> balise() == "EnsemblesRaciaux" )
    {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre d'ensembles raciaux" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      const ElementDOM* ens( Lecture.premierEnfant( noeud ) );

      do
	{
	  if ( ! lireString( ens -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << capa -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;
	  
	  sc = lireEnsemble( ens );
	  if ( ! sc )
	    {
	      error() << sc.message() << endmsg;
	      func() << "MetaRace::lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les ensembles raciaux" );
	    }
	  
	  info() << EnsemblesRaciaux.back() << endmsg;
	  
	  ens = Lecture.prochainFrere( ens );
	}
      while ( ens != 0 );

      if ( EnsemblesRaciaux.size() != nbrElement + 1 )
	warning() << "Problème de lecture des capacités raciales : "
		  << "on attend " << nbrElement + 1 << " items, mais "
		  << EnsemblesRaciaux.size() << " ont été trouvés" << endmsg;
      
      nbrElement = 0;
      
      noeud = Lecture.prochainFrere( noeud );
    }
  
  NombreTotalEnsembles = EnsemblesRaciaux.size();

  if ( noeud != 0 && noeud -> balise() == "Ajustements" )
     {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre d'ensembles raciaux" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

       const ElementDOM* mod( Lecture.premierEnfant( noeud ) );

       do
	 {
	   if ( ! lireString( mod -> attribut( "num" ), uIntTmp ) )
	     warning() << "Ne peut pas lire l'attribut \"num\" de "
		       << capa -> balise() << endmsg;
	   else if ( uIntTmp > nbrElement )
	     nbrElement = uIntTmp;
	   
	   sc = lireModificateur( mod );
	   if ( ! sc )
	     {
	       sc.afficher();
	       func() << "MetaRace::lireFichier ==> fin" << endmsg;
	       /*Debug::*/depileNiveau();
	       return StatusCode( false, 0, CodesErreurs::ErreurConstructionObjetComplexe,
				  "lireFichier : impossible d'initialiser les ajustements raciaux" );
	     }
	   
	   mod = Lecture.prochainFrere( mod );
	 }
       while ( mod != 0 );

       map< unsigned, int >::const_iterator it, End( AjustementsRaciaux.end() );
       int total( 0 );

       Caracteristique caracteristique;

       for (it = AjustementsRaciaux.begin(); it != End; it++)
	 {
	   caracteristique.setValeur( it -> first );
	   info() << caracteristique << " : " << it -> second << endmsg;
	   total += it -> second;
	 }
       
       if ( AjustementsRaciaux.size() != nbrElement + 1 )
	 warning() << "Problème de lecture des capacités raciales : "
		   << "on attend " << nbrElement + 1 << " items, mais "
		   << AjustementsRaciaux.size() << " ont été trouvés" << endmsg;

       if ( total != 0 )
	 warning() << "Problème : la somme des ajustement n'est pas nulle : "
		   << total << endmsg;
       
       nbrElement = 0;
       
       noeud = Lecture.prochainFrere( noeud );
     }

  if ( noeud != 0 && noeud -> balise() == "Special" )
    {
      unsigned trait, comp;
      int ajust;

      const ElementDOM* special( Lecture.premierEnfant( noeud ) );

      if ( ! lireString( special -> donnees(), trait ) )
	{
	  error() << "Ne peut pas lire le trait concerné" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			     "lireFichier : impossible de lire le bonus" );
	}

      special = Lecture.prochainFrere( special );

      if ( ! lireString( special -> donnees(), comp ) )
	{
	  error() << "Ne peut pas lire la compétence déclenchante" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			     "lireFichier : impossible de lire le bonus" );
	}

      special = Lecture.prochainFrere( special );

      if ( ! lireString( special -> donnees(), ajust ) )
	{
	  error() << "Ne peut pas lire le bonus au trait" << endmsg;
	  func() << "MetaRace::lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireInt,
			     "lireFichier : impossible de lire le bonus" );
	}

      BonusAuTrait = new BonusConditionnel;
      BonusAuTrait -> TraitNumero = trait;
      BonusAuTrait -> CapaNumero  = comp;
      BonusAuTrait -> Ajustement  = ajust;

      info() << "Si \"" << CapacitesRaciales[ comp ] . nom() 
	     << "\" est sélectionné, "
	     << "alors le trait " << Caracteristique( trait )
	     << " a un bonus de " << ajust << endmsg;
    }

  func() << "MetaRace::lireFichier ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return sc;
}

/**
 * Réalise l'allocation mémoire d'un objet du type correspondant à la
 * Race demandée.
 *
 * @return un pointeur sur la Race voulue.
 */
Race* MetaRace::creerObjet()
{
  if ( Type == "Humain" )
    return new Humain( this );
  else if ( Type == "Demi-Elfe" )
    return new DemiElfe( this );
  else if ( Type == "Demi-Ogre" )
    return new DemiOgre( this );
  else if ( Type == "Demi-Orque" )
    return new DemiOrque( this );
  else if ( Type == "Elfe" )
    return new Elfe( this );
  else if ( Type == "Gnome" )
    return new Gnome( this );
  else if ( Type == "Hobbit" )
    return new Hobbit( this );
  else if ( Type == "Nain" )
    return new Nain( this );
  else
    return 0;
}

/**
 * Lit le contenu d'un prérequis racial, càd le trait concerné, les
 * valeurs minimale et maximale.
 *
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher les messages de fin et de début de
 * méthode.
 *
 * @param[in] elDom pointeur sur l'élément DOM dont la balise est
 * "Prerequis".
 *
 * @return StatusCode, en cas d'erreur, le champ message contient un
 * minimum d'information permettant d'identifier le problème.
 */
StatusCode MetaRace::lirePrerequis(const ElementDOM* elDom)
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "MetaRace::lirePrerequis ==> début" << endmsg;

  unsigned trait, min, max;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  if ( ! lireString( enfant -> donnees(), trait ) )
    {
      func() << "MetaRace::lirePrerequis ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lirePrerequis : trait illisible" );
    }

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireString( enfant -> donnees(), min ) )
    {
      func() << "MetaRace::lirePrerequis ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lirePrerequis : min illisible" );
    }

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireString( enfant -> donnees(), max ) )
    {
      func() << "MetaRace::lirePrerequis ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lirePrerequis : max illisible" );
    }

  PrerequisRaciaux.push_back( Prerequis( trait, min, max ) );

  func() << "MetaRace::lirePrerequis ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode();
}

/**
 * Lit le contenu d'une capacité raciale, càd le nom et le coût.
 *
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher les messages de fin et de début de
 * méthode.
 *
 * @param[in] elDom pointeur sur l'élément DOM dont la balise est
 * "Capacite".
 *
 * @return StatusCode, en cas d'erreur, le champ message contient un
 * minimum d'information permettant d'identifier le problème.
 */
StatusCode MetaRace::lireCapa(const ElementDOM* elDom)
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "MetaRace::lireCapa ==> début" << endmsg;

  string nom;
  unsigned prix;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  nom = enfant -> donnees();

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireString( enfant -> donnees(), prix ) )
    {
      func() << "MetaRace::lireCapa ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lireCapa : coût illisible" );
    }

  CapacitesRaciales.push_back( CapaciteRaciale( nom, prix ) );

  func() << "MetaRace::lireCapa ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode();
}

/**
 * Lit le contenu d'un ensemble de capacités raciales, càd le nom, le
 * coût et les capacités contenues dans l'ensemble (les numéros
 * uniquement).
 *
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher les messages de fin et de début de
 * méthode.
 *
 * @param[in] elDom pointeur sur l'élément DOM dont la balise est
 * "Ensemble".
 *
 * @return StatusCode, en cas d'erreur, le champ message contient un
 * minimum d'information permettant d'identifier le problème.
 */
StatusCode MetaRace::lireEnsemble(const ElementDOM* elDom)
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "MetaRace::lireEnsemble ==> début" << endmsg;

  string nom;
  unsigned prix;
  vector< unsigned > membresEns;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  nom = enfant -> donnees();

  enfant = Lecture.prochainFrere( enfant );
  
  if ( ! lireString( enfant -> donnees(), prix ) )
    {
      func() << "MetaRace::lireEnsemble ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lireEnsemble : coût illisible" );
    }

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireVectorUInt( enfant -> donnees(), &membresEns ) )
    {
      func() << "MetaRace::lireEnsemble ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureVecteurUint,
			 "lireEnsemble : membres illisibles" );
    }

  EnsemblesRaciaux.push_back( EnsembleCapacitesRaciales( nom, prix, membresEns ) );
  
  func() << "MetaRace::lireEnsemble ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode();
}

/**
 * Lit les éventuels ajustement raciaux. Il faut lire ici le trait
 * concerné et le modificateur.
 *
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher les messages de fin et de début de
 * méthode.
 *
 * @param[in] elDom pointeur sur l'élément DOM dont la balise est
 * "Modificateur".
 *
 * @return StatusCode, en cas d'erreur, le champ message contient un
 * minimum d'information permettant d'identifier le problème.
 */
StatusCode MetaRace::lireModificateur(const ElementDOM* elDom)
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "MetaRace::lireModificateur ==> début" << endmsg;

  unsigned trait;
  int mod;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  if ( ! lireString( enfant -> donnees(), trait ) )
    {
      func() << "MetaRace::lireModificateur ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			 "lireModificateur : trait illisible" );
    }

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireString( enfant -> donnees(), mod ) )
    {
      func() << "MetaRace::lireModificateur ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
    return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireInt,
		       "lireModificateur : valeur illisible" );
    }

  AjustementsRaciaux.insert( make_pair< unsigned, int >( trait, mod ) );
  
  func() << "MetaRace::lireModificateur ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode();
}
